﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class represents a mathematical vector with n components.
    /// </summary>
    [Serializable]
    public class GeneralVector : AbstractGeneralVector, IGeneralVector
    {
        /// <summary>
        /// This array saves the componentdatas.
        /// </summary>
        private double[] vectorData;

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector"/> class.
        /// </summary>
        /// <param name="vector">The vector to clone.</param>
        public GeneralVector(GeneralVector vector)
        {
            if (vector == (GeneralVector)null)
            {
                throw new ArgumentNullException("vector");
            }

            this.vectorData = vector.VectorData;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector"/> class.
        /// </summary>
        /// <param name="dimension">The dimension of the vector.</param>
        public GeneralVector(int dimension)
        {
            if (dimension < 0)
            {
                throw new ArgumentException("dimension < 0", "dimension");
            }

            this.vectorData = new double[dimension];
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector"/> class.
        /// </summary>
        /// <param name="data">The componentdatas of the vector in form of an array.</param>
        public GeneralVector(double[] data)
        {
            if (data == (double[])((object)null))
            {
                throw new ArgumentNullException("data");
            }

            this.vectorData = data;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector"/> class.
        /// </summary>
        /// <param name="point">The point which should be included into the vector.</param>
        public GeneralVector(Point2D point)
        {
            if (point == (Point2D)null)
            {
                throw new ArgumentNullException("point");
            }

            this.vectorData = new double[2];
            this.vectorData[0] = point.X;
            this.vectorData[1] = point.Y;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector"/> class.
        /// </summary>
        /// <param name="point">The point which should be included into the vector.</param>
        public GeneralVector(Point3D point)
        {
            if (point == (Point3D)null)
            {
                throw new ArgumentNullException("point");
            }

            this.vectorData = new double[3];
            this.vectorData[0] = point.X;
            this.vectorData[1] = point.Y;
            this.vectorData[2] = point.Z;
        }

        /// <summary>
        /// Gets or sets the <see cref="System.Double"/> value of the vector at the specified index.
        /// </summary>
        /// <value>The component of the vector at the specified position.</value>
        public double this[int index]
        {
            get { return this.GetComponent(index); }
            set { this.SetComponent(index, value); }
        }

        /// <summary>
        /// Gets or sets the vector componentdatas.
        /// </summary>
        /// <value>The vector componentdatas.</value>
        public double[] VectorData
        {
            get { return vectorData; }
            set { vectorData = value; }
        }

        /// <summary>
        /// Counts the components of the vector.
        /// </summary>
        /// <value>The number of components of the vector.</value>
        public int Count
        {
            get { return vectorData.Length; }
        }

        /// <summary>
        /// Creates the sign inverted vector of the current instance.
        /// </summary>
        /// <returns>The sign inverted vector of the current instance.</returns>
        public GeneralVector CreateSignInvertedVector()
        {
            GeneralVector result = new GeneralVector(this.Count);

            for (int i = 0; i < this.Count; i++)
            {
                result[i] = -this[i];
            }

            return result;
        }

        /// <summary>
        /// Copies this instance.
        /// </summary>
        /// <returns>The current instance of the vector.</returns>
        public GeneralVector Copy()
        {
            return new GeneralVector((double[])this.vectorData.Clone());
        }

        /// <summary>
        /// Gets the component value.
        /// </summary>
        /// <param name="index">The index of the component.</param>
        /// <returns>The vectorcomponent value at the over given index</returns>
        public double GetComponent(int index)
        {
            if ((uint)index >= (uint)(this.vectorData.Length))
            {
                throw new ArgumentException("complex reason");
            }

            return this.vectorData[index];
        }

        /// <summary>
        /// Sets the component value at a specified position.
        /// </summary>
        /// <param name="index">The index of the component.</param>
        /// <param name="value">The new value of the component.</param>
        public void SetComponent(int index, double value)
        {
            if ((uint)index >= (uint)(this.vectorData.Length))
            {
                throw new ArgumentException("complex reason");
            }

            this.vectorData[index] = value;
        }

        /// <summary>
        /// Gets the length of the vector.
        /// </summary>
        /// <value>The length of the vector.</value>
        public double Length
        {
            get
            {
                double result = 0;

                for (int i = 0; i < this.vectorData.Length; i++)
                {
                    result += Math.Pow(this.vectorData[i], 2);
                }

                return Math.Sqrt(result);
            }
        }

        /// <summary>
        /// Normalizes the current vector.
        /// </summary>
        public void Normalize()
        {
            double tempuri = 0.0;

            for (int i = 0; i < this.Count; i++)
            {
                tempuri += Math.Pow(this[i], 2);
            }

            tempuri = Math.Sqrt(tempuri);

            for (int i = 0; i < this.Count; i++)
            {
                this[i] = this[i] / tempuri;
            }
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector operator +(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a == (GeneralVector)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                throw new ArithmeticException("Number of components in both vectors has to be even.");
            }

            double[] result = new double[a.Count];

            for (int i = 0; i < a.Count; i++)
            {
                result[i] = a.GetComponent(i) + b.GetComponent(i);
            }

            return new GeneralVector(result);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector operator -(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                throw new ArithmeticException("Number of components in both vectors have to be even.");
            }

            double[] result = new double[a.Count];

            for (int i = 0; i < a.Count; i++)
            {
                result[i] = a.GetComponent(i) - b.GetComponent(i);
            }

            return new GeneralVector(result);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The vector.</param>
        /// <param name="value">The scalar value.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector operator *(GeneralVector a, double value)
        {
            if (a == (GeneralVector)null)
            {
                throw new ArgumentNullException("a");
            }

            double[] result = new double[a.Count];

            for (int i = 0; i < a.Count; i++)
            {
                result[i] = a.GetComponent(i) * value;
            }

            return new GeneralVector(result);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="value">The scalar value.</param>
        /// <param name="a">The vector.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector operator *(double value, GeneralVector a)
        {
            if (a == (GeneralVector)null)
            {
                throw new ArgumentNullException("a");
            }

            double[] result = new double[a.Count];

            for (int i = 0; i < a.Count; i++)
            {
                result[i] = a.GetComponent(i) * value;
            }

            return new GeneralVector(result);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static double operator *(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                throw new ArithmeticException("Number of components in both vectors have to be even.");
            }

            double result = 0;

            for (int i = 0; i < a.Count; i++)
            {
                result += a.GetComponent(i) * b.GetComponent(i);
            }

            return result;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(GeneralVector a, GeneralVector b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object)a == null) || ((object)b == null))
            {
                return false;
            }

            if (a.Count != b.Count)
            {
                return false;
            }

            for (int i = 0; i < a.Count; i++)
            {
                if (a.GetComponent(i) != b.GetComponent(i))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(GeneralVector a, GeneralVector b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Implements the operator &lt;.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                return false;
            }

            for (int i = 0; i < a.Count; i++)
            {
                if (a.GetComponent(i) >= b.GetComponent(i))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Implements the operator &lt;=.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <=(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                return false;
            }

            for (int i = 0; i < a.Count; i++)
            {
                if (a.GetComponent(i) > b.GetComponent(i))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Implements the operator &gt;.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                return false;
            }

            for (int i = 0; i < a.Count; i++)
            {
                if (a.GetComponent(i) <= b.GetComponent(i))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Implements the operator &gt;=.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >=(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                return false;
            }

            for (int i = 0; i < a.Count; i++)
            {
                if (a.GetComponent(i) < b.GetComponent(i))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Generates a vector with random components.
        /// </summary>
        /// <param name="components">The number of components.</param>
        /// <returns>The generated vector with random components.</returns>
        public static GeneralVector GetRandomVector(int components)
        {
            if (components < 0)
            {
                throw new ArgumentException("components < 0", "components");
            }

            Random random = new Random();

            double[] randomDatas = new double[components];

            for (int i = 0; i < components; i++)
            {
                randomDatas[i] = random.Next();
            }

            return new GeneralVector(randomDatas);
        }

        /// <summary>
        /// Unions the specified two vectors.
        /// </summary>
        /// <param name="a">The first vector to union.</param>
        /// <param name="b">The second vector to union.</param>
        /// <returns>The two source vector values in one vector.</returns>
        public static GeneralVector UnionVectors(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("b");
            }

            GeneralVector result = new GeneralVector(a.Count + b.Count);

            for (int i = 0; i < a.Count; i++)
            {
                result[i] = a[i];
            }

            int add = a.Count;

            for (int i = 0; i < b.Count; i++)
            {
                result[i + add] = b[i];
            }

            return result;
        }

        /// <summary>
        /// Computes the dot product of two vectors.
        /// </summary>
        /// <param name="a">The first vector.</param>
        /// <param name="b">The second vector.</param>
        /// <returns>The dot product of both vectors.</returns>
        public static double ComputeDotProduct(GeneralVector a, GeneralVector b)
        {
            if (a == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector2D)null)
            {
                throw new ArgumentNullException("b");
            }

            if (a.Count != b.Count)
            {
                throw new ArithmeticException("The number of components has to be even to compute the dot product.");
            }

            double tempuri = 0;

            for (int i = 0; i < a.Count; i++)
            {
                tempuri += a[i] * b[i];
            }

            return tempuri;
        }

        /// <summary>
        /// Determines whether the current vector is orthogonal to the specified vector.
        /// </summary>
        /// <param name="value">The specified vector to check.</param>
        /// <returns>
        /// True if current vector is orthogonal to the specified vector otherwise, false.
        /// </returns>
        public bool IsOrthogonalTo(GeneralVector value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            if (this.Count != value.Count)
            {
                return false;
            }

            double check = 0;

            for (int i = 0; i < this.Count; i++)
            {
                check += this[i] * value[i];
            }

            return check == 0;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Converts the current instance of the vector to a row matrix.
        /// </summary>
        /// <returns>The converted matrix.</returns>
        public Matrix ToMatrix()
        {
            Matrix matrix = new Matrix(this.vectorData.Length, 1);

            for (int i = 0; i < this.vectorData.Length; i++)
            {
                matrix.SetValueAtPosition(new MatrixPosition(i, 0), this.vectorData[i]);
            }

            return matrix;
        }

        /// <summary>
        /// Converts the data of the vector to an array of numeric values.
        /// </summary>
        /// <returns>An array of numeric values.</returns>
        public double[] ToArray()
        {
            return (double[])this.vectorData.Clone();
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            String result = "";

            for (int i = 0; i < this.vectorData.Length; i++)
            {
                result += this.vectorData[i] + "; ";
            }

            if (result.Length > 2)
            {
                result = result.Substring(0, result.Length - 2);
            }

            return result;
        }
    }
}